//给你一个字符串 s 和一个字符规律 p，请你来实现一个支持 '.' 和 '*' 的正则表达式匹配。
//
//
// '.' 匹配任意单个字符
// '*' 匹配零个或多个前面的那一个元素
//
//
// 所谓匹配，是要涵盖 整个 字符串 s的，而不是部分字符串。
//
// 示例 1：
//
//
//输入：s = "aa", p = "a"
//输出：false
//解释："a" 无法匹配 "aa" 整个字符串。
//
//
// 示例 2:
//
//
//输入：s = "aa", p = "a*"
//输出：true
//解释：因为 '*' 代表可以匹配零个或多个前面的那一个元素, 在这里前面的元素就是 'a'。因此，字符串 "aa" 可被视为 'a' 重复了一次。
//
//
// 示例 3：
//
//
//输入：s = "ab", p = ".*"
//输出：true
//解释：".*" 表示可匹配零个或多个（'*'）任意字符（'.'）。
//
//
//
//
// 提示：
//
//
// 1 <= s.length <= 20
// 1 <= p.length <= 20
// s 只包含从 a-z 的小写字母。
// p 只包含从 a-z 的小写字母，以及字符 . 和 *。
// 保证每次出现字符 * 时，前面都匹配到有效的字符
//
//
// Related Topics 递归 字符串 动态规划 👍 3603 👎 0


//leetcode submit region begin(Prohibit modification and deletion)
function isMatch(s: string, p: string): boolean {

    if (s == null || p == null) return false;

    const sLen = s.length, pLen = p.length;
    //? 二维数组dp[i][j]表示s(0,i - 1)和p(0,j - 1)是否匹配
    const dp = new Array(sLen + 1);
    for (let i = 0; i < dp.length; i++) {
        dp[i] = new Array(pLen + 1).fill(false); //? 将项默认为false
    }
    // base case
    dp[0][0] = true;
    for (let j = 1; j < pLen + 1; j++) {
        //? 存在星号 则需要继续往前看 由于*不会出现到开头 故dp[0][j - 2]没问题
        if (p[j - 1] == "*") dp[0][j] = dp[0][j - 2];
    }
    //? 迭代
    for (let i = 1; i < sLen + 1; i++) {
        for (let j = 1; j < pLen + 1; j++) {
            //? 相等 延续上一状态
            if (s[i - 1] == p[j - 1] || p[j - 1] == ".") {
                dp[i][j] = dp[i - 1][j - 1];
            }
            //? 是* 又要分情况
            else if (p[j - 1] == "*") {
                //? 又存在p[j - 1]的*使p[j - 2]消失、出现一次和出现>=2次的情况
                //? 消失的情况 即j失去了两个 考察s(0,i - 1)和p(0,j - 3)
                //? 重复一次的情况 j-2和i-1处会抵消 考察p(0,j-3)和s(0,i-2)
                //? 重复2+次 拿出一个与s[i - 1]抵消(p此时末尾仍是*) 继续考察s(0,i - 2)和p(0,j - 1)
                if (s[i - 1] == p[j - 2] || p[j - 2] == ".") {
                    //? 三种情况有一个为真即可为真
                    dp[i][j] = dp[i][j - 2] || dp[i - 1][j - 2] || dp[i - 1][j];
                } else {
                    //? p[j - 1]的*可以干掉p[j - 2] 继续考察s(0,i - 1)和p(0,j - 3)
                    dp[i][j] = dp[i][j - 2];
                }
            }
        }
    }
    return dp[sLen][pLen]; //? 长sLen的s串 是否匹配 长pLen的p串

};
//leetcode submit region end(Prohibit modification and deletion)
